﻿namespace MobileTrack.Web.Tests.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Principal;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using System.Web.Security;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using MobileTrack.Core;
    using MobileTrack.Core.Data;
    using MobileTrack.Core.Model;
    using MobileTrack.Web.Controllers;
    using MobileTrack.Web.ViewModels;
    using Moq;

    [TestClass]
    public class AbonosControllerFixture
    {
        private IList<Marca> marcas;
        private IList<Abono> abonos;
        private string userRole;
        private bool isInRole;

        private Mock<IRepository<Marca>> marcasRepositoryMock;
        private Mock<IRepository<Abono>> abonosRepositoryMock;
        private Mock<IRepositories> repositoriesMock;
        private Mock<IPrincipal> principalMock;

        [TestInitialize]
        public void TestInitialize()
        {
            this.marcas = new List<Marca>();
            this.abonos = new List<Abono>();
            this.userRole = string.Empty;
            this.isInRole = false;

            this.marcasRepositoryMock = new Mock<IRepository<Marca>>();
            this.marcasRepositoryMock.Setup(mr => mr.All).Returns(() => this.marcas.AsQueryable());

            this.abonosRepositoryMock = new Mock<IRepository<Abono>>();
            this.abonosRepositoryMock.Setup(ar => ar.All).Returns(() => this.abonos.AsQueryable());

            this.repositoriesMock = new Mock<IRepositories>();
            this.repositoriesMock.Setup(rs => rs.Marcas).Returns(() => this.marcasRepositoryMock.Object);
            this.repositoriesMock.Setup(rs => rs.Abonos).Returns(() => this.abonosRepositoryMock.Object);

            this.principalMock = new Mock<IPrincipal>();
            this.principalMock.Setup(p => p.IsInRole(It.Is<string>(r => r.Equals(this.userRole, StringComparison.OrdinalIgnoreCase)))).Returns(() => this.isInRole);
        }

        [TestMethod]
        public void IndexShouldReturnFilterAndOperadoresInViewBag()
        {
            this.marcas.Add(new Marca { Id = 1, Codigo = 10, Nombre = "Marca1", EsOperador = true });
            this.marcas.Add(new Marca { Id = 2, Codigo = 20, Nombre = "Marca2", EsOperador = true });
            this.marcas.Add(new Marca { Id = 3, Codigo = 30, Nombre = "Marca3", EsOperador = false });

            var controller = this.CreateAbonosController();

            var result = controller.Index("producto:samsung", 1) as ViewResult;
            Assert.IsNotNull(result);

            var filter = result.ViewBag.Filter as string;
            var operadores = result.ViewBag.OperadoresFilter as IEnumerable<string>;
            Assert.AreEqual("producto:samsung", filter);
            Assert.IsTrue(operadores.Count() == 2);
            Assert.IsTrue(operadores.Contains("Marca1"));
            Assert.IsTrue(operadores.Contains("Marca2"));
            Assert.IsFalse(operadores.Contains("Marca3"));
        }

        [TestMethod]
        public void AddAbonoShouldReturnOperadoresInViewBag()
        {
            this.marcas.Add(new Marca { Id = 1, Codigo = 10, Nombre = "Marca1", EsOperador = true });
            this.marcas.Add(new Marca { Id = 2, Codigo = 20, Nombre = "Marca2", EsOperador = true });
            this.marcas.Add(new Marca { Id = 3, Codigo = 30, Nombre = "Marca3", EsOperador = false });

            var controller = this.CreateAbonosController();

            var result = controller.AddAbono() as ViewResult;
            Assert.IsNotNull(result);

            var operadores = result.ViewBag.Operadores as IEnumerable<SelectListItem>;
            Assert.IsTrue(operadores.Count() == 2);
            Assert.IsTrue(operadores.Any(i => i.Text == "Marca1 (10)"));
            Assert.IsTrue(operadores.Any(i => i.Text == "Marca2 (20)"));
            Assert.IsFalse(operadores.Any(i => i.Text == "Marca3 (30)"));
        }

        [TestMethod]
        public void AddAbonoShouldReturnPartialViewWhenAjaxRequest()
        {
            var controller = this.CreateAbonosControllerWithAjaxRequest();
            var result = controller.AddAbono() as PartialViewResult;
            Assert.IsNotNull(result);
            Assert.AreEqual("_AddAbono", result.ViewName);
        }

        [TestMethod]
        public void AddAbonoShouldSaveAbonoForAdministrador()
        {
            this.userRole = Constants.AdministradorRole;
            this.isInRole = true;

            this.marcas.Add(new Marca { Id = 1, Codigo = 10, Nombre = "Marca1", EsOperador = true });

            var model = new AddAbonoViewModel
            {
                Descripcion = "Abono1",
                FechaInicio = DateTime.Today,
                FechaFin = DateTime.Today.AddDays(1),
                OperadorId = 1,
                Precio = 100
            };

            var controller = this.CreateAbonosController();

            var result = controller.AddAbono(model) as JsonResult;
            Assert.IsNotNull(result);

            var json = result.Data.ToString();
            Assert.AreEqual("{ saved = True }", json);
        }

        [TestMethod]
        public void AddAbonoShouldReturnPartialViewOnInvalidModelForAdministrator()
        {
            this.userRole = Constants.AdministradorRole;
            this.isInRole = true;

            this.marcas.Add(new Marca { Id = 1, Codigo = 10, Nombre = "Marca1", EsOperador = true });
            this.marcas.Add(new Marca { Id = 2, Codigo = 20, Nombre = "Marca2", EsOperador = true });
            this.marcas.Add(new Marca { Id = 3, Codigo = 30, Nombre = "Marca3", EsOperador = false });

            var today = DateTime.Today;
            var model = new AddAbonoViewModel
            {
                Descripcion = "Abono1",
                FechaInicio = today,
                FechaFin = today.AddDays(1),
                OperadorId = 1,
                Precio = 100
            };

            var controller = this.CreateAbonosController();
            var state = new ModelState();
            state.Errors.Add("Error1");
            controller.ModelState.Add("Error1", state);

            var result = controller.AddAbono(model) as PartialViewResult;
            Assert.IsNotNull(result);
            Assert.AreEqual("_AddAbono", result.ViewName);

            var passedModel = result.Model as AddAbonoViewModel;
            Assert.AreEqual("Abono1", passedModel.Descripcion);
            Assert.AreEqual(today, passedModel.FechaInicio);
            Assert.AreEqual(today.AddDays(1), passedModel.FechaFin);
            Assert.AreEqual(1, passedModel.OperadorId);
            Assert.AreEqual(100, passedModel.Precio);

            var operadores = result.ViewBag.Operadores as IEnumerable<SelectListItem>;
            Assert.IsTrue(operadores.Count() == 2);
            Assert.IsTrue(operadores.Any(i => i.Text == "Marca1 (10)"));
            Assert.IsTrue(operadores.Any(i => i.Text == "Marca2 (20)"));
            Assert.IsFalse(operadores.Any(i => i.Text == "Marca3 (30)"));
        }

        [TestMethod]
        public void EditAbonoShouldReturnOperadoresInViewBag()
        {
            this.marcas.Add(new Marca { Id = 1, Codigo = 10, Nombre = "Marca1", EsOperador = true });
            this.marcas.Add(new Marca { Id = 2, Codigo = 20, Nombre = "Marca2", EsOperador = true });
            this.marcas.Add(new Marca { Id = 3, Codigo = 30, Nombre = "Marca3", EsOperador = false });

            var controller = this.CreateAbonosController();

            var result = controller.EditAbono() as ViewResult;
            Assert.IsNotNull(result);

            var operadores = result.ViewBag.Operadores as IEnumerable<SelectListItem>;
            Assert.IsTrue(operadores.Count() == 2);
            Assert.IsTrue(operadores.Any(i => i.Text == "Marca1 (10)"));
            Assert.IsTrue(operadores.Any(i => i.Text == "Marca2 (20)"));
            Assert.IsFalse(operadores.Any(i => i.Text == "Marca3 (30)"));
        }

        [TestMethod]
        public void EditAbonoShouldReturnPartialViewWhenAjaxRequest()
        {
            var controller = this.CreateAbonosControllerWithAjaxRequest();
            var result = controller.EditAbono() as PartialViewResult;
            Assert.IsNotNull(result);
            Assert.AreEqual("_EditAbono", result.ViewName);
        }

        [TestMethod]
        public void EditAbonoShouldSaveAbonoForAdministrador()
        {
            this.userRole = Constants.AdministradorRole;
            this.isInRole = true;

            var today = DateTime.Today;
            this.marcas.Add(new Marca { Id = 1, Codigo = 10, Nombre = "Marca1", EsOperador = true });
            this.abonos.Add(new Abono { Id = 1, Descripcion = "Abono1", FechaInicio = today, FechaFin = today.AddDays(1), Precio = 200, MarcaId = 1 });

            var model = new EditAbonoViewModel
            {
                Id = 1,
                Descripcion = "Abono2",
                FechaInicio = today,
                FechaFin = today.AddDays(1),
                OperadorId = 1,
                Precio = 200
            };

            var controller = this.CreateAbonosController();

            var result = controller.EditAbono(model) as JsonResult;
            Assert.IsNotNull(result);

            var json = result.Data.ToString();
            Assert.AreEqual("{ saved = True }", json);
        }

        [TestMethod]
        public void EditAbonoShouldReturnPartialViewOnInvalidModelForAdministrator()
        {
            this.userRole = Constants.AdministradorRole;
            this.isInRole = true;

            var today = DateTime.Today;
            this.marcas.Add(new Marca { Id = 1, Codigo = 10, Nombre = "Marca1", EsOperador = true });
            this.marcas.Add(new Marca { Id = 2, Codigo = 20, Nombre = "Marca2", EsOperador = true });
            this.marcas.Add(new Marca { Id = 3, Codigo = 30, Nombre = "Marca3", EsOperador = false });
            this.abonos.Add(new Abono { Id = 1, Descripcion = "Abono1", FechaInicio = today, FechaFin = today.AddDays(1), Precio = 200, MarcaId = 1 });

            var model = new EditAbonoViewModel
            {
                Id = 1,
                Descripcion = "Abono2",
                FechaInicio = today,
                FechaFin = today.AddDays(1),
                OperadorId = 1,
                Precio = 200
            };

            var controller = this.CreateAbonosController();
            var state = new ModelState();
            state.Errors.Add("Error1");
            controller.ModelState.Add("Error1", state);

            var result = controller.EditAbono(model) as PartialViewResult;
            Assert.IsNotNull(result);
            Assert.AreEqual("_EditAbono", result.ViewName);

            var passedModel = result.Model as EditAbonoViewModel;
            Assert.AreEqual("Abono2", passedModel.Descripcion);
            Assert.AreEqual(today, passedModel.FechaInicio);
            Assert.AreEqual(today.AddDays(1), passedModel.FechaFin);
            Assert.AreEqual(1, passedModel.OperadorId);
            Assert.AreEqual(200, passedModel.Precio);

            var operadores = result.ViewBag.Operadores as IEnumerable<SelectListItem>;
            Assert.IsTrue(operadores.Count() == 2);
            Assert.IsTrue(operadores.Any(i => i.Text == "Marca1 (10)"));
            Assert.IsTrue(operadores.Any(i => i.Text == "Marca2 (20)"));
            Assert.IsFalse(operadores.Any(i => i.Text == "Marca3 (30)"));
        }

        [TestMethod]
        public void DeleteAbonoShouldDeleteAbonosForAdministrador()
        {
            this.userRole = Constants.AdministradorRole;
            this.isInRole = true;

            var today = DateTime.Today;
            this.marcas.Add(new Marca { Id = 1, Codigo = 10, Nombre = "Marca1", EsOperador = true });
            this.abonos.Add(new Abono { Id = 1, Descripcion = "Abono1", FechaInicio = today, FechaFin = today.AddDays(1), Precio = 100, MarcaId = 1 });
            this.abonos.Add(new Abono { Id = 2, Descripcion = "Abono2", FechaInicio = today, FechaFin = today.AddDays(1), Precio = 200, MarcaId = 1 });
            this.abonos.Add(new Abono { Id = 3, Descripcion = "Abono3", FechaInicio = today, FechaFin = today.AddDays(1), Precio = 300, MarcaId = 1 });

            var controller = this.CreateAbonosController();

            int[] abonos = new int[] { 1, 2 };
            var result = controller.DeleteAbono(abonos) as JsonResult;
            Assert.IsNotNull(result);

            var json = result.Data.ToString();
            Assert.AreEqual("{ deleted = True }", json);
        }

        [TestMethod]
        public void ListShouldReturnAbonos()
        {
            DateTime today = DateTime.Today;
            this.marcas.Add(new Marca { Id = 1, Codigo = 10, Nombre = "Marca1", EsOperador = true });
            this.abonos.Add(new Abono { Id = 1, Descripcion = "Abono1", FechaInicio = today, FechaFin = today.AddDays(1), Precio = 100, MarcaId = 1 });
            this.abonos.Add(new Abono { Id = 2, Descripcion = "Abono2", FechaInicio = today, FechaFin = today.AddDays(1), Precio = 200, MarcaId = 1 });
            this.abonos.Add(new Abono { Id = 3, Descripcion = "Abono3", FechaInicio = today, FechaFin = today.AddDays(1), Precio = 300, MarcaId = 1 });

            var controller = this.CreateAbonosController();

            var result = controller.List(string.Empty, 1, 50) as JsonResult;
            Assert.IsNotNull(result);

            var model = result.Data;
            Assert.IsNotNull(model);

            //// TODO: Deserialize Json and assert for specific content
        }

        [TestMethod]
        public void ListShouldReturnFilteredAbonosWhenFilterIsNotEmpty()
        {
            DateTime today = DateTime.Today;
            this.marcas.Add(new Marca { Id = 1, Codigo = 10, Nombre = "Marca1", EsOperador = true });
            this.abonos.Add(new Abono { Id = 1, Descripcion = "Abono1", FechaInicio = today, FechaFin = today.AddDays(1), Precio = 100, MarcaId = 1 });
            this.abonos.Add(new Abono { Id = 2, Descripcion = "Abono2", FechaInicio = today, FechaFin = today.AddDays(1), Precio = 200, MarcaId = 1 });
            this.abonos.Add(new Abono { Id = 3, Descripcion = "Abono3", FechaInicio = today, FechaFin = today.AddDays(1), Precio = 300, MarcaId = 1 });

            var controller = this.CreateAbonosController();

            var result = controller.List("id:1+precio:100+descripcion:abono1+operador:Marca1+inicio:2012-12-12+fin:2012-12-12", 1, 50) as JsonResult;
            Assert.IsNotNull(result);

            var model = result.Data;
            Assert.IsNotNull(model);

            //// TODO: Deserialize Json and assert for specific content
        }

        private AbonosController CreateAbonosController(MembershipProvider membershipProvider = null, RoleProvider roleProvider = null)
        {
            var httpContextMock = new Mock<HttpContextBase>();
            httpContextMock.Setup(c => c.User).Returns(() => this.principalMock.Object);
            httpContextMock.Setup(c => c.Request).Returns(new Mock<HttpRequestBase>().Object);
            httpContextMock.Setup(c => c.Response).Returns(new Mock<HttpResponseBase>().Object);

            var httpContext = httpContextMock.Object;

            var routes = new RouteCollection();
            RouteConfig.RegisterRoutes(routes);

            var controller = new AbonosController(this.repositoriesMock.Object, membershipProvider, roleProvider)
            {
                ControllerContext = new ControllerContext { HttpContext = httpContext },
                Url = new UrlHelper(new RequestContext(httpContext, new RouteData()), routes),
            };

            return controller;
        }

        private AbonosController CreateAbonosControllerWithAjaxRequest(MembershipProvider membershipProvider = null, RoleProvider roleProvider = null)
        {
            var httpContextMock = new Mock<HttpContextBase>();
            httpContextMock.Setup(c => c.User).Returns(() => this.principalMock.Object);
            var requestMock = new Mock<HttpRequestBase>();
            requestMock.Setup(r => r["X-Requested-With"]).Returns("XMLHttpRequest");
            httpContextMock.Setup(c => c.Request).Returns(requestMock.Object);
            httpContextMock.Setup(c => c.Response).Returns(new Mock<HttpResponseBase>().Object);

            var httpContext = httpContextMock.Object;

            var routes = new RouteCollection();
            RouteConfig.RegisterRoutes(routes);

            var controller = new AbonosController(this.repositoriesMock.Object, membershipProvider, roleProvider)
            {
                ControllerContext = new ControllerContext { HttpContext = httpContext },
                Url = new UrlHelper(new RequestContext(httpContext, new RouteData()), routes),
            };

            return controller;
        }

        private void VerifyAll()
        {
            this.marcasRepositoryMock.VerifyAll();
            this.repositoriesMock.VerifyAll();
            this.principalMock.VerifyAll();
        }

        private Celular CreateCelular(long imei, Estado lastEstado, string lastSucursalName)
        {
            var today = DateTime.Today;
            var celular = new Celular { Imei = imei };
            celular.Seguimientos.Add(new Seguimiento { Id = 1, Estado = Estado.Pendiente, Fecha = today, Sucursal = new Sucursal { Nombre = "SampleSucName1", Codigo = "SampleSucCod1" } });
            celular.Seguimientos.Add(new Seguimiento { Id = 2, Estado = lastEstado, Fecha = today, Sucursal = new Sucursal { Nombre = lastSucursalName, Codigo = "SampleSucCod2" } });

            return celular;
        }
    }
}
